﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32.SafeHandles;
using NHook.Native;

namespace NHook
{
	public class ThreadDebugger : Commandable
	{
		private readonly ProcessDebugger _ProcessDebugger;
		public ProcessDebugger ProcessDebugger
		{
			get
			{
				return _ProcessDebugger;
			}
		}
		SafeFileHandle _ThreadHandle;

		public SafeFileHandle Handle
		{
			get
			{
				return _ThreadHandle;
			}
		}
		public ThreadDebugger(SafeFileHandle threadHandle, ProcessDebugger processDebugger, uint threadId)
		{
			_Wait = new Wait(this);
			_Continue = new Continue(this);
			_ThreadHandle = threadHandle;
			_ProcessDebugger = processDebugger;
			ThreadId = threadId;
		}

		public DebugEvent CurrentEvent
		{
			get;
			internal set;
		}
		public void GoTo(VA va)
		{
			NHook.Native.Helper.ChangeEIPTo(Handle, va.ToIntPtr());
		}

		public VA CurrentInstruction
		{
			get
			{
				return new VA(ThreadContext.EIP);
			}
		}

		public bool StepByStep
		{
			get;
			set;
		}

		private readonly Wait _Wait;
		public Wait Wait
		{
			get
			{
				return _Wait;
			}
		}
		private readonly Continue _Continue;
		public Continue Continue
		{
			get
			{
				return _Continue;
			}
		}

		public ThreadContext ThreadContext
		{
			get
			{
				return NHook.Native.Helper.GetThreadContext(Handle);
			}
		}


		public uint ThreadId
		{
			get;
			private set;
		}


		#region Commandable Members

		Wait Commandable.Wait
		{
			get
			{
				return Wait;
			}
		}

		Continue Commandable.Continue
		{
			get
			{
				return Continue;
			}
		}

		ProcessDebugger Commandable.ProcessDebugger
		{
			get
			{
				return ProcessDebugger;
			}
		}

		ThreadDebugger Commandable.GetCurrentThreadDebugger()
		{
			return this;
		}

		DebugEvent Commandable.CurrentEvent
		{
			get
			{
				return CurrentEvent;
			}
		}

		#endregion

		const int MAXCMDSIZE = 16;
		public Patch WriteInstruction(string instruction, bool fillWithNop = true)
		{
			Patch patcher = _ProcessDebugger.Patches.New(CurrentInstruction);
			var oldBytes = MemoryUtil.ReadMemory(
						ProcessDebugger.Debuggee.Information.ProcessHandle.DangerousGetHandle(),
						CurrentInstruction.ToIntPtr(),
						MAXCMDSIZE);
			var oldInstruction = DisasmWrapper.Disasm(oldBytes);
			var newInstruction = patcher.Write(instruction);
			if(fillWithNop)
			{
				for(int i = 0 ; i < oldInstruction.Bytes.Length - newInstruction.Bytes.Length ; i++)
				{
					patcher.Write("NOP");
				}
			}
			return patcher;
		}
	}
}
